var { sin, cos, atan2, sqrt, abs, PI } = Math;

var baiduFactor = PI * 3000.0 / 180.0;

function BD09toGCJ02( coord ) {

    var [ lon, lat ] = coord;

    var x = lon - 0.0065;
    var y = lat - 0.006;
    var z = sqrt( x * x + y * y ) - 0.00002 * sin( y * baiduFactor );
    var theta = atan2( y, x ) - 0.000003 * cos( x * baiduFactor );
    var newLon = z * cos( theta );
    var newLat = z * sin( theta );

    return [ newLon, newLat ];
}

function GCJ02toBD09( coord ) {

    var [ lon, lat ] = coord;

    var x = lon;
    var y = lat;
    var z = sqrt( x * x + y * y ) + 0.00002 * sin( y * baiduFactor );
    var theta = atan2( y, x ) + 0.000003 * cos( x * baiduFactor );

    var newLon = z * cos( theta ) + 0.0065;
    var newLat = z * sin( theta ) + 0.006;

    return [ newLon, newLat ];

}


function isInChina( lon, lat ) {
	return lon >= 72.004 && lon <= 137.8347 && lat >= 0.8293 && lat <= 55.8271;
}

var a = 6378245;
var ee = 0.006693421622965823;

function GCJ02toWGS84( coord ) {

    var [ lon, lat ] = coord;

    if ( !isInChina( lon, lat ) ) return [ lon, lat ];

    var [ wgsLon, wgsLat ] = [ lon, lat ];

    var tempPoint = WGS84toGCJ02( [ wgsLon, wgsLat ] );

    var dx = tempPoint[ 0 ] - lon;
    var dy = tempPoint[ 1 ] - lat;

    while ( abs( dx ) > 1e-6 || abs( dy ) > 1e-6 ) {
        wgsLon -= dx;
        wgsLat -= dy;

        tempPoint = WGS84toGCJ02( [ wgsLon, wgsLat ] );
        dx = tempPoint[ 0 ] - lon;
        dy = tempPoint[ 1 ] - lat;
    }

    return [ wgsLon, wgsLat ];
}

function WGS84toGCJ02( coord ) {

    var [ lon, lat ] = coord;

    if ( !isInChina( lon, lat ) ) return [ lon, lat ];

    var d = delta( lon, lat );

    return [ lon + d[ 0 ], lat + d[ 1 ] ];

}

function transformLat( x, y ) {
    var ret = -100 + 2 * x + 3 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * sqrt( abs( x ) );
    ret += ( 20 * sin( 6 * x * PI ) + 20 * sin( 2 * x * PI ) ) * 2 / 3;
    ret += ( 20 * sin( y * PI ) + 40 * sin( y / 3 * PI ) ) * 2 / 3;
    ret += ( 160 * sin( y / 12 * PI ) + 320 * sin( y * PI / 30 ) ) * 2 / 3;
    return ret;
}

function transformLon( x, y ) {
    var ret = 300 + x + 2 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * sqrt( abs( x ) );
    ret += ( 20 * sin( 6 * x * PI ) + 20 * sin( 2 * x * PI ) ) * 2 / 3;
    ret += ( 20 * sin( x * PI ) + 40 * sin( x / 3 * PI ) ) * 2 / 3;
    ret += ( 150 * sin( x / 12 * PI ) + 300 * sin( x / 30 * PI ) ) * 2 / 3;
    return ret;
}

function delta( lon, lat ) {

    var dLon = transformLon( lon - 105, lat - 35 );
    var dLat = transformLat( lon - 105, lat - 35 );

    var radLat = lat / 180 * PI;
    var magic = sin( radLat );

    magic = 1 - ee * magic * magic;

    var sqrtMagic = sqrt( magic );
    dLon = ( dLon * 180 ) / ( a / sqrtMagic * cos( radLat ) * PI );
    dLat = ( dLat * 180 ) / ( ( a * ( 1 - ee ) ) / ( magic * sqrtMagic ) * PI );

    return [ dLon, dLat ];
}




/**
 * transform
 *
 * @param {geojson|position} input
 * @returns {geojson|position} output
 */
function transform( input, fromCRS, toCRS ) {
    var from = CRS[ fromCRS ];

    var to = from.to[ toCRS ];

    var type = typeof( input );

    if ( type === 'string' ) input = JSON.parse( input );

    var isPosition = false;
    isPosition = true;

    var output = null;
    var convert = to;

    if ( isPosition ) {
        output = convert( input );
    } else {
        coordEach( input, function( coord ) {
            var newCoord = convert( coord );
            coord[ 0 ] = newCoord[ 0 ];
            coord[ 1 ] = newCoord[ 1 ];
        } );

        output = input;
    }

    return output;
}
